package top.v5it.japi.ddd.core.dao.impl.wrapper;

import cn.hutool.core.lang.func.Func1;
import cn.hutool.core.util.ArrayUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.Arrays;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * 统计包装
 *
 * @param <T> 实体类型
 * @param <W> 子类类型，用于方法链式调用
 * @date 2023/3/31
 */
@Slf4j
public class StatisticsWrapper<T, W extends StatisticsWrapper<T, W>> extends ColumnWrapper<T, W> {

    protected StatisticsWrapper(T entity) {
        super(entity);
    }

    private void fillSegment(String funcName, String column, String alias) {
        final String template = "%s(`%s`) AS %s";
        addColumnSegment(String.format(template, funcName, column, alias));
    }

    /**
     * 添加求和统计列
     *
     * @param column 列名
     * @param alias  别名
     * @return 当前实例
     */
    public W sum(String column, String alias) {
        fillSegment("SUM", column, alias);
        return wrapper;
    }

    /**
     * 添加求和统计列
     *
     * @param columnFunc 列的获取函数
     * @param alias      别名
     * @return 当前实例
     */
    public W sum(Func1<T, ?> columnFunc, String alias) {
        return this.sum(this.mapper(columnFunc), alias);
    }

    /**
     * 添加计数统计列
     *
     * @param column 列名
     * @param alias  别名
     * @return 当前实例
     */
    public W count(String column, String alias) {
        fillSegment("COUNT", column, alias);
        return wrapper;
    }

    /**
     * 添加计数统计列
     *
     * @param columnFunc 列的获取函数
     * @param alias      别名
     * @return 当前实例
     */
    public W count(Func1<T, ?> columnFunc, String alias) {
        return this.count(this.mapper(columnFunc), alias);
    }

    /**
     * 添加最大值统计列
     *
     * @param column 列名
     * @param alias  别名
     * @return 当前实例
     */
    public W max(String column, String alias) {
        fillSegment("MAX", column, alias);
        return wrapper;
    }

    /**
     * 添加最大值统计列
     *
     * @param columnFunc 列的获取函数
     * @param alias      别名
     * @return 当前实例
     */
    public W max(Func1<T, ?> columnFunc, String alias) {
        return this.max(this.mapper(columnFunc), alias);
    }

    /**
     * 添加最小值统计列
     *
     * @param column 列名
     * @param alias  别名
     * @return 当前实例
     */
    public W min(String column, String alias) {
        fillSegment("MIN", column, alias);
        return wrapper;
    }

    /**
     * 添加最小值统计列
     *
     * @param columnFunc 列的获取函数
     * @param alias      别名
     * @return 当前实例
     */
    public W min(Func1<T, ?> columnFunc, String alias) {
        return this.min(this.mapper(columnFunc), alias);
    }

    /**
     * 添加平均值统计列
     *
     * @param column 列名
     * @param alias  别名
     * @return 当前实例
     */
    public W avg(String column, String alias) {
        fillSegment("AVG", column, alias);
        return wrapper;
    }

    /**
     * 添加平均值统计列
     *
     * @param columnFunc 列的获取函数
     * @param alias      别名
     * @return 当前实例
     */
    public W avg(Func1<T, ?> columnFunc, String alias) {
        return this.avg(this.mapper(columnFunc), alias);
    }

    /**
     * 添加分组列
     *
     * @param columns 列名数组
     * @return 当前实例
     */
    public W group(String... columns) {
        if (ArrayUtil.isEmpty(columns)) return wrapper;
        Arrays.stream(columns).forEach(this::column); // 添加分组列当作查询输出
        final String collect = Arrays.stream(columns)
                .collect(Collectors.joining("`, `", "`", "`"));
        final String groupBy = String.format("GROUP BY %s", collect);
        addSegment("group", groupBy);
        return wrapper;
    }

    /**
     * 添加分组列
     *
     * @param columnFuncs 列的获取函数数组
     * @return 当前实例
     */
    @SafeVarargs
    public final W group(Func1<T, ?>... columnFuncs) {
        if (ArrayUtil.isEmpty(columnFuncs)) return wrapper;
        final String[] columns = Arrays.stream(columnFuncs).map(this::mapper).toArray(String[]::new);
        return this.group(columns);
    }

    /**
     * 添加HAVING条件
     *
     * @param supplier 自定义的HAVING条件语句函数
     * @return 当前实例
     */
    public W having(Supplier<String> supplier) {
        final String having = String.format("HAVING %s", supplier.get());
        addSegment("having", having);
        return wrapper;
    }
}
